#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <assert.h>
#include <getopt.h>

#include "../error.h"
#include "../main.h"
#include "../util.h"
#include "probe.h"
#include "../action/probe.h"

#define DEFAULT_PROBE_ADDRESS   "http://connect.rom.miui.com/generate_204"
#define DEFAULT_PROBE_MODE      PROBE_MODE_STATUS
#define DEFAULT_PROBE_CONTENT   ""
#define DEFAULT_PROBE_STATUS    204

_Noreturn void verb_probe_usage(FILE *restrict out) {
    fputs("Usage: " PROGRAM_NAME " probe [OPTIONS]\n", out);
    fputs("Probe the network connectivity and print the url of the login page if login is needed.\n", out);
    fputs("\n", out);
    fputs("Options:\n", out);
    fputs("  -a, --address=ADDRESS      use the address\n", out);
    fputs("  -c, --content=TEXT         match the text content TEXT\n", out);
    fputs("  -m, --mode=MODE            match the result by MODE; more info below\n", out);
    fputs("  -s, --status=STATUS        match the status code STATUS\n", out);
    fputs("  -h, --help                 display this help and exit\n", out);
    fputs("\n", out);
    fputs("There are three matching modes: status, content, status-and-content.\n", out);
    fputs("\n", out);
    fputs("The status mode only checks for HTTP status code, and the content mode\n", out);
    fputs("only checks for the content, while the status-and-content checks both of\n", out);
    fputs("them.\n", out);
    fputs("\n", out);
    fputs("Exit status:\n", out);
    fputs(" 0  if successfully matched probe page\n", out);
    fputs(" 1  if unable to parse arguments\n", out);
    fputs(" 2  if probe failed\n", out);
    fputs(" 3  if matched login page\n", out);
    fputs(" 4  if matched nothing\n", out);
    exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
}

int verb_probe(const int argc, char* const* argv) {
    int lose = 0;
    static struct option long_options[] = {
        {"address",   required_argument,  NULL, 'a' },
        {"content",   required_argument,  NULL, 'c' },
        {"mode",      required_argument,  NULL, 'm' },
        {"status",    required_argument,  NULL, 's' },
        {"help",      no_argument,        NULL, 'h' },
        {0,           0,                  0,     0 }
    };
    char*             probe_address = DEFAULT_PROBE_ADDRESS;
    action_probe_mode probe_mode    = DEFAULT_PROBE_MODE;
    char*             probe_content = DEFAULT_PROBE_CONTENT;
    int               probe_status  = DEFAULT_PROBE_STATUS;
    
    while (1) {
        int c = getopt_long(argc, argv, "a:c:m:s:h", long_options, NULL);

        if (c == -1) {
            break;
        }

        switch (c) {
            case 'h':
                verb_probe_usage(stdout);
                break;
        
            case 'a':
                probe_address = optarg;
                break;

            case 'c':
                probe_content = optarg;
                break;

            case 'm':
                static const char* modes[] = {
                    "status",
                    "content",
                    "status-and-content"
                };
                int mode = find_string(optarg, modes, sizeof(modes) / sizeof(char*));
                if (mode == -1) {
                    error(0, 0, "Unknown mode: %s", optarg);
                    error(EXIT_FAILURE, 0, "Avaliable modes: status, content, status-and-content");
                }
                probe_mode = (action_probe_mode) mode;
                break;

            case 's':
                int status = sscanf(optarg, "%d", &probe_status);
                if (status != 1) {
                    if (status == 0) {
                        error(EXIT_FAILURE, 0, "Invaild status code: %s", optarg);
                    } else if (errno == ERANGE) {
                        error(EXIT_FAILURE, 0, "Status code out of range: %s", optarg);
                    } else {
                        error(EXIT_FAILURE, 0, "Unable to read the status code: %s", optarg);
                    }
                }
                break;

            default:
                lose = 1;
                break;
        }
    }

    if (lose) {
        verb_probe_usage(stderr);
        return EXIT_FAILURE;
    }
    
    if (optind < argc) {
        error(0, 0, "Too many arguments.");
        verb_probe_usage(stderr);
        return EXIT_FAILURE;
    }
    
    action_probe_result result = action_probe(
        GLOBAL_ARGS.bind_interface,
        probe_address,
        probe_mode,
        probe_content,
        probe_status
    );
    switch (result.status) {
        case ACTION_PROBED_SUCCESSFULLY:
            action_probe_result_free(&result);
            return 0;
        case ACTION_PROBED_LOGIN_PAGE:
            printf("Host: %s\n", result.host);
            printf("Search: %s\n", result.search);
            action_probe_result_free(&result);
            return 3;
        case ACTION_PROBED_NOTHING:
            action_probe_result_free(&result);
            return 4;
        case ACTION_PROBEING_FAILURE:
            action_probe_result_free(&result);
            return 2;
        default:
            assert(0);
            exit(1);
            break;
    }
}
